#include "ustyleditemdelegate.h"
#include "uapplication.h"
#include "style/uproxystyle.h"
#include "style/upalette.h"
#include "utkwidgetglobal.h"

#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>

constexpr int margins = 3;

UStyledItemDelegate::UStyledItemDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}

UStyledItemDelegate::~UStyledItemDelegate()
{
}

void UStyledItemDelegate::setCheckColumnIndex(int index)
{
    m_checkColumnIndex = index;
}

void UStyledItemDelegate::setRowHeight(int height)
{
    m_rowHeight = height;
}

int UStyledItemDelegate::rowHeight()
{
    return m_rowHeight;
}

void UStyledItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (!index.isValid())
    {
        QStyledItemDelegate::paint(painter, option, index);
        return;
    }

    painter->save();
    painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);

    QRect rect = opt.rect;
    QFontMetrics fm(opt.font);
    QRect textRect = rect;

    QPalette::ColorGroup group = UProxyStyle::colorGroup(&opt, opt.widget);

    UProxyStyle* style = qobject_cast<UProxyStyle*>(uApp->style());
    int radius = style ? style->pixelMetric(UProxyStyle::PM_FrameRadius, &option) : 8;

    // 设置背景色、文本色
    UPalette* palette = uApp->applicationPalette();
    QBrush background;
    QPen foreground = painter->pen();
    foreground.setColor(palette->color(group, QPalette::Text));
    background = palette->color(group, UPalette::ItemBackground);
    bool hasAlternatefeature = opt.features & QStyleOptionViewItem::Alternate;
    if (!hasAlternatefeature)
    {
        background = palette->color(group, UPalette::AlternateBase);
    }
    if (opt.state & QStyle::State_Enabled)
    {
        if (opt.state & QStyle::State_Selected)
        {
            background = palette->color(group, QPalette::Highlight);
            foreground.setColor(palette->color(group, QPalette::HighlightedText));
        }
        else if (opt.state & QStyle::State_MouseOver)
        {
            background = palette->color(QPalette::Button);
        }
    }

    painter->fillPath(UProxyStyle::backgroundPath(opt, radius), background);

    if (index.column() == m_checkColumnIndex)
    {
        QStyleOptionButton option;
        initCheckBoxStyleOption(&option, index);
        option.rect = checkBoxRect(rect, QSize(16, 16));
        if (style)
        {
            style->drawControl(UProxyStyle::UControlElement::CE_CheckBox, &option, painter, nullptr);
        }
        painter->restore();
        return;
    }

    int topMargin = 4;
    QRect iconRect = QRect(rect.left() + 10, rect.top() + topMargin,
                           textRect.height() - topMargin*2, textRect.height() - topMargin*2);
    QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
    if (!icon.isNull())
    {
        int iconWidth = textRect.height() - topMargin*2;
        textRect.adjust(10 + iconWidth + 8, 0, 0, 0);
        icon.paint(painter, iconRect, Qt::AlignCenter);
    }
    else
    {
        textRect.adjust(10, 0, 0, 0);
    }

    bool isChecked = index.data(Qt::CheckStateRole).toBool();
    if (isChecked)
    {
        textRect.setWidth(textRect.width() - m_width - 10);
        QRect checkRect = QRect(rect.x() + rect.width() - m_width - 10, rect.y() + (rect.height() - m_width) / 2,
                                m_width, m_width);
        QStyleOptionViewItem viewItemCheck = opt;
        viewItemCheck.rect = checkRect;
        viewItemCheck.state |= QStyle::State_On;
        if (style)
        {
            style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &viewItemCheck, painter, opt.widget);
        }
    }
    if (QVariant::Color == index.data().type()) {
        int height = textRect.height() - radius;
        int width = textRect.width() - radius * 2;
        QPainterPath path;
        path.addRoundedRect(QRect(textRect.x() + radius, textRect.y() + radius / 2, width, height), radius, radius);
        painter->fillPath(path, index.data().value<QColor>());
    } else {
        // 绘制文本
        QString text = fm.elidedText(opt.text, Qt::ElideMiddle, textRect.width());
        painter->setFont(opt.font);
        painter->setPen(foreground);
        painter->drawText(textRect, Qt::TextSingleLine | static_cast<int>(opt.displayAlignment), text);
    }
    painter->restore();
}

QSize UStyledItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QSize size = QStyledItemDelegate::sizeHint(option, index);

    return QSize(size.width(), m_rowHeight);
}

void UStyledItemDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex &index) const
{
    QStyledItemDelegate::initStyleOption(option, index);
}

void UStyledItemDelegate::drawChecked(QPainter* painter, const QRect &rect, const QColor &color) const
{
    painter->drawEllipse(rect);
    painter->setPen(QPen(color, 2));
    painter->drawLine(rect.center().x() - 4, rect.center().y(), rect.center().x(), rect.center().y() + 5);
    painter->drawLine(rect.center().x(), rect.center().y() + 5, rect.center().x() + 6, rect.center().y() - 3);
}

void UStyledItemDelegate::initCheckBoxStyleOption(QStyleOptionButton* option, const QModelIndex &index) const
{
    if (!option)
        return;

    option->state |= QStyle::State_Enabled;

    if (index.data(SunkenRole).toBool())
    {
        option->state |= QStyle::State_Sunken;
    }

    option->state |= index.data().toBool() ? QStyle::State_On : QStyle::State_Off;

    option->state.setFlag(QStyle::State_MouseOver, index.data(MouseOverRole).toBool());
}

bool UStyledItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (!index.isValid() || (index.column() != m_checkColumnIndex))
        return QStyledItemDelegate::editorEvent(event, model, option, index);

    switch (event->type())
    {
        case QEvent::MouseButtonPress:
        {
            QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
            if ((mouseEvent->buttons() & Qt::LeftButton) && (index.column() == m_checkColumnIndex))
            {
                QRect rect = checkBoxRect(option.rect, QSize(16, 16));
                model->setData(index, rect.contains(mouseEvent->pos()), SunkenRole);
            }
            break;
        }
        case QEvent::MouseMove:
        {
            QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
            if (index.column() == m_checkColumnIndex)
            {
                QRect rect = checkBoxRect(option.rect, QSize(16, 16));
                model->setData(index, rect.contains(mouseEvent->pos()), MouseOverRole);
            }
            break;
        }
        case QEvent::MouseButtonRelease:
        {
            QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
            return handleMouseEvent(mouseEvent, model, option, index);
        }
        default:
            return QStyledItemDelegate::editorEvent(event, model, option, index);
    }

    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

bool UStyledItemDelegate::handleMouseEvent(QMouseEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (index.data(SunkenRole).toBool())
    {
        model->setData(index, !index.data().toBool());
        model->setData(index, false, SunkenRole);
        emit checkStatusChanged(index, index.data().toBool());
        return true;
    }

    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

QRect UStyledItemDelegate::checkBoxRect(const QRect &rect, const QSize &size) const
{
    return QRect(rect.x() + (rect.width() - size.width()) / 2,
                 rect.y() + (rect.height() - size.height()) / 2, size.width(), size.height());
}

